/**
 * @file mmgr_misc.c
 * @brief 
 * @author  ()
 * @version 1.0
 * @date 2024-09-13
 * 
 * @copyright Copyright (C) 2019 Queclink Wireless Solutions (ShenZhen) Co., Ltd. All Rights Reserved.
 * 
 * @par 修改日志:
 * <table>
 * <tr><th>Date       <th>Version <th>Author  <th>Description
 * <tr><td>2024-09-13 <td>1.0     <td>     <td>内容
 * </table>
 */

/* includes ---------------------------------------------------------------------------------------------------------*/
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include "include/mmgr_log.h"
#include <mmgr/include/task_mmgr.h>
#include <mmgr/include/mmgr_misc.h>
#include <modem/qlmodem/task_modem.h>
#include "misc/rtc_app.h"

/*-------------------------------------------------------------------------------------------------------------------*/
/*
 * 模块控制参数
 */
struct misc_cps {
    bool                rtc_had_sync_net;       /* 是否通过网络进行同步 */
    bool                is_sq_upd_success;      /* 信号强度最近一次更新是否成功 */
    bool                is_net_upd_success;     /* 网络注册最近一次更新是否成功 */
    bool                is_cell_upd_success;    /* 小区信息最近一次更新是否成功 */
    bool                is_ipaddr_upd_success;  /* PDP地址最近一次更新是否成功 */
    bool                is_rsrp_upd_success;    /* RSRP最近一次更新是否成功 */
    int32_t             rssi;                   /* 当前rssi值 */
    int32_t             ber;                    /* 当前ber值 */
    int32_t             rsrp;                   /* 当前rsrp值 */
    int32_t             net_status;             /* 当前net_status值 */
    int32_t             rat_state;              /* 当前rat_state值 */
    struct cell_info    srv_cell;               /* 当前小区信息 */
    uint8_t             nbr_count;              /* 临近小区个数 <= 5 */
    struct cell_info    nbr_cell[5];            /* 临近小区信息 */
    struct local_ipaddr ipaddr;                 /* PDP地址信息 */
    uint32_t            sq_upd_last_time;       /* 最近一次更新时间 */
    uint32_t            net_upd_last_time;      /* 最近一次更新时间 */
    uint32_t            cell_upd_last_time;     /* 最近一次更新时间 */
    uint32_t            ipaddr_upd_last_time;   /* 最近一次更新时间 */
};

struct misc_cps _cps;
static struct misc_cps *misc_cps = &_cps;

#define SQ_CHK_INTERVAL             60          /* 查询信号质量间隔, 单位s */
#define UPDE_NET_STATUS_TIME        60          /* 查询网络注册间隔, 单位s */
#define UPDE_CELL_INFO_TIME         60          /* 查询网络注册间隔, 单位s */
#define IPADDR_CHK_INTERVAL         60          /* 查询PDP地址间隔, 单位s */

/*************************************************************************
 * 函数名称：misc_cps_restore_defult
 * 函数功能：清除所有的查询信息
 * 输入参数：void
 * 返 回 值：void
 *************************************************************************/
static void misc_cps_restore_defult(void)
{
    misc_cps->rtc_had_sync_net      = false;
    misc_cps->is_sq_upd_success     = false;
    misc_cps->is_net_upd_success    = false;
    misc_cps->is_cell_upd_success   = false;
    misc_cps->is_ipaddr_upd_success = false;
    misc_cps->rssi                  = 0;
    misc_cps->ber                   = 0;
    misc_cps->net_status            = NETREG_NO;
    misc_cps->rat_state             = NET_INVAL;

    memset(&misc_cps->srv_cell, 0, sizeof(struct cell_info));
    for(uint8_t i = 0; i < sizeof(misc_cps->nbr_cell) / sizeof(misc_cps->nbr_cell[0]); i++)
    {
        memset(&misc_cps->nbr_cell[i], 0, sizeof(struct cell_info));
    }
    memset(&misc_cps->ipaddr, 0, sizeof(struct local_ipaddr));
    misc_cps->sq_upd_last_time      = NOTIMING;
    misc_cps->net_upd_last_time     = NOTIMING;
    misc_cps->cell_upd_last_time    = NOTIMING;
    misc_cps->ipaddr_upd_last_time  = NOTIMING;
}

/*************************************************************************
 * 函数名称：mmgr_reset_sq_upd_time
 * 函数功能：csq查询时间清除，以便快速重新查询
 * 输入参数：void
 * 返 回 值：void
 *************************************************************************/
void mmgr_reset_sq_upd_time(void)
{
    misc_cps->sq_upd_last_time      = NOTIMING;
}

/*************************************************************************
 * 函数名称：sq_status_update
 * 函数功能：定时查询csq状态
 * 输入参数：void
 * 返 回 值：void
 *************************************************************************/
static void csq_status_update(void)
{
    int32_t new_rssi = 0;
    int32_t new_ber  = 0;
    int32_t delay    = 0;
    int32_t new_rsrp = 0;

    if (!misc_cps->is_sq_upd_success)
        delay = SQ_CHK_INTERVAL / 3;
    else
        delay = SQ_CHK_INTERVAL;    /* 若失败,采用1/3的扫描时间,进行下次更新. */

    if (NOTIMING == misc_cps->sq_upd_last_time ||
        timeout_sec(misc_cps->sq_upd_last_time, delay)) {
        int32_t res = qltask_modem_ioctl(MDM_GET_CSQ, &new_rssi, &new_ber, NULL);
        if (0 != res) {
            misc_cps->is_sq_upd_success = false;

        } else {
            misc_cps->rssi  = new_rssi;
            misc_cps->ber   = new_ber;
            misc_cps->is_sq_upd_success = true;

            log_d("GET CSQ:%d-%d\r\n", new_rssi, new_ber);
        }

        res = qltask_modem_ioctl(MDM_GET_RSRP, &new_rsrp);
        if (0 != res) {
            misc_cps->is_rsrp_upd_success = false;
        } else {
            misc_cps->rsrp = new_rsrp;
            misc_cps->is_rsrp_upd_success = true;

            log_d("GET RSRP:%d\r\n", new_rsrp);
        }
        misc_cps->sq_upd_last_time = getsec();
    }
}

/*************************************************************************
 * 函数名称：netreg_status_update
 * 函数功能：定时查询更新网络注册状态和网络制式
 * 输入参数：void
 * 返 回 值：void
 *************************************************************************/
static void netreg_status_update(void)
{
    int32_t net_status;
    int32_t rat_state;
    int32_t delay = 0;

    if (!misc_cps->is_net_upd_success)
        delay = UPDE_NET_STATUS_TIME / 3;     /* 若失败,采用1/3的扫描时间,进行下次更新. */
    else
        delay = UPDE_NET_STATUS_TIME;

    if (NOTIMING == misc_cps->net_upd_last_time ||
        timeout_sec(misc_cps->net_upd_last_time, delay)) {
        int32_t res = qltask_modem_ioctl(MDM_GET_NETREG, &net_status, &rat_state, NULL);
        if (0 != res) {
            misc_cps->is_net_upd_success = false;
            misc_cps->net_status         = NETREG_NO;
            misc_cps->rat_state          = NET_INVAL;

            if (misc_cps->is_sq_upd_success)
                misc_cps->sq_upd_last_time = NOTIMING;  /* 若前面已经获取成功,则重新更新SQ值. */

        } else {

            misc_cps->net_status = net_status;
            misc_cps->rat_state  = rat_state;

            if (NETREG_NO == misc_cps->net_status ||
                NET_INVAL == misc_cps->rat_state) {
                misc_cps->is_net_upd_success = false;
            } else {
                misc_cps->is_net_upd_success = true;
            }

            if (!misc_cps->is_sq_upd_success)
                misc_cps->sq_upd_last_time   = NOTIMING;  /* 若前面已经获取失败,,重新更新SQ值. */

        }
        misc_cps->net_upd_last_time = getsec();
    }
}

/*************************************************************************
 * 函数名称：cell_info_update
 * 函数功能：定时查询小区信息
 * 输入参数：void
 * 返 回 值：void
 *************************************************************************/
static void cell_info_update(void)
{
    struct cell_info    cell[6] = {0};
    int8_t              count   = 0;
    int32_t             delay   = 0;

    if (!misc_cps->is_cell_upd_success || 0 == misc_cps->nbr_count)
        delay = UPDE_CELL_INFO_TIME / 3;
    else
        delay = UPDE_CELL_INFO_TIME;    /* 若失败,采用1/3的扫描时间,进行下次更新. */

    if (NOTIMING == misc_cps->cell_upd_last_time ||
        timeout_sec(misc_cps->cell_upd_last_time, delay)) {
        int32_t res = qltask_modem_ioctl(MDM_GET_CELL, cell, &count);

        if (0 != res) {
            misc_cps->is_cell_upd_success = false;
            log_e("Cell info update failed!\r\n");

        } else {
            /* 第一个为当前小区信息 */
            memcpy(&misc_cps->srv_cell, &cell[0], sizeof(struct cell_info));
            misc_cps->is_cell_upd_success = true;
            log_i("Cell info update:mcc = %04d, mnc = %04d, lac = %04x, cellid = %08x\r\n",
                                                    misc_cps->srv_cell.mcc,
                                                    misc_cps->srv_cell.mnc,
                                                    misc_cps->srv_cell.lac,
                                                    misc_cps->srv_cell.cellid);

            /* 保存邻近小区信息 */
            count = 6 < count? 6 : count;
            misc_cps->nbr_count = count-1;
            for(uint8_t i = 0; i < misc_cps->nbr_count; i++) {
                memcpy(&misc_cps->nbr_cell[i], &cell[i+1], sizeof(struct cell_info));
                log_i("Nbr Cell info update:mcc = %04d, mnc = %04d, lac = %04x, cellid = %08x\r\n",
                                                    misc_cps->nbr_cell[i].mcc,
                                                    misc_cps->nbr_cell[i].mnc,
                                                    misc_cps->nbr_cell[i].lac,
                                                    misc_cps->nbr_cell[i].cellid);
            }
        }
        misc_cps->cell_upd_last_time = getsec();
    }
}

/*************************************************************************
 * 函数名称：pdp_ip_update
 * 函数功能：定时查询PDP地址
 * 输入参数：void
 * 返 回 值：void
 *************************************************************************/
static void pdp_ip_update(void)
{
    struct local_ipaddr ipaddr = {0};
    int32_t delay = 0;

    if (!misc_cps->is_ipaddr_upd_success)
        delay = IPADDR_CHK_INTERVAL / 3;
    else
        delay = IPADDR_CHK_INTERVAL;    /* 若失败,采用1/3的扫描时间,进行下次更新. */

    if (NOTIMING == misc_cps->ipaddr_upd_last_time ||
        timeout_sec(misc_cps->ipaddr_upd_last_time, delay)) {
        int32_t res = qltask_modem_ioctl(MDM_GET_LOCAL_IPADDR, &ipaddr);
        if (0 != res) {
            misc_cps->is_ipaddr_upd_success = false;

        } else {
            if (0 != ipaddr.type) {
                memset(&misc_cps->ipaddr, 0, sizeof(struct local_ipaddr));
                memcpy(&misc_cps->ipaddr, &ipaddr, sizeof(struct local_ipaddr));
                misc_cps->is_ipaddr_upd_success = true;
                if (1 == ipaddr.type)
                {
                    log_i("pdp ip type is ipv4, addr:%s\r\n", ipaddr.ipv4);
                }
                else if (2 == ipaddr.type)
                {
                    log_i("pdp ip type is ipv6, addr:%s\r\n", ipaddr.ipv6);
                }
                else if (3 == ipaddr.type)
                {
                    log_i("pdp ip type is ipv4v6, addr:%s/%s\r\n", ipaddr.ipv4, ipaddr.ipv6);
                }
            }
        }
        misc_cps->ipaddr_upd_last_time = getsec();
    }

}


/*************************************************************************
 * 函数名称：mmgr_netreg_update
 * 函数功能：网络注册状态更新处理
 * 输入参数：prm-新注册状态, data-新网络类型, size-无意义
 * 返 回 值：void
 *************************************************************************/
void mmgr_netreg_update(int32_t prm, void *data, size_t size)
{
    if (misc_cps->net_status == prm &&
        misc_cps->rat_state == (int32_t)data)       /* 没有更新 */
        return;

    misc_cps->net_status = prm;
    misc_cps->rat_state = (int32_t)data;

    if (NETREG_NO == misc_cps->net_status ||
        NET_INVAL == misc_cps->rat_state) {
        misc_cps->is_net_upd_success = false;
        misc_cps->net_upd_last_time = getsec();
        misc_cps->sq_upd_last_time = NOTIMING;  /* 重新更新SQ值. */
    } else {
        misc_cps->is_net_upd_success = true;
        misc_cps->net_upd_last_time = NOTIMING; /* 说明此次已经注册上网络,故实时更新band. */
        misc_cps->sq_upd_last_time = NOTIMING;  /* 重新更新SQ值. */
    }
}

/*************************************************************************
 * 函数名称：modem_get_signal_quality
 * 函数功能：获取网络信号质量
 * 输入参数：void
 * 返 回 值：负数-获取失败，0-获取成功
 *************************************************************************/
int32_t modem_get_signal_quality(int32_t *rssi, int32_t *ber)
{
    int32_t res = 0;
    if (!misc_cps->is_sq_upd_success) {
        res = -ENODATA;
    } else {
        if (NULL != rssi)
            *rssi = misc_cps->rssi;

        if (NULL != ber)
            *ber = misc_cps->ber;
    }
    return res;
}

/* 获取非2G信号值 */
int32_t modem_get_rsrp(int32_t *rsrp)
{
    int32_t res = 0;
    if (!misc_cps->is_rsrp_upd_success) {
        res = -ENODATA;
    } else {
        if (NULL != rsrp)
            *rsrp = misc_cps->rsrp;
    }
    return res;
}

/*************************************************************************
 * 函数名称：modem_get_netreg_status
 * 函数功能：获取网络注册状态和网络制式
 * 输入参数：void
 * 返 回 值：负数-获取失败，0-获取成功
 *************************************************************************/
int32_t modem_get_netreg_status(int32_t *net_status, int32_t *rat_state)
{
    int32_t res = 0;
    if (!misc_cps->is_net_upd_success) {
        res = -ENODATA;
    } else {
        if (NULL != net_status)
            *net_status = misc_cps->net_status;

        if (NULL != rat_state)
            *rat_state = misc_cps->rat_state;
    }

    return res;
}

/**
 * @brief 
 * @param  cell             My Param doc
 * @param  nbr_cell         My Param doc
 * @param  nbr_count        My Param doc
 * @return int32_t 
 */
int32_t modem_get_cell_info(struct cell_info *cell, struct cell_info (*nbr_cell)[5], uint8_t *nbr_count)
{
    if (NULL == cell || nbr_cell == NULL || nbr_count == NULL)
        return -EIO;

    int32_t res = 0;
    if (!misc_cps->is_cell_upd_success)
        res = -ENODATA;
    else{
        memcpy(cell, &misc_cps->srv_cell, sizeof(struct cell_info));
        if(nbr_cell){
            for(uint8_t i = 0; i < misc_cps->nbr_count; i++){
                memcpy(&nbr_cell[i], &misc_cps->nbr_cell[i], sizeof(struct cell_info));
            }

            if(nbr_count){
                *nbr_count = misc_cps->nbr_count;
            }
        }

        res = 0;
    }

    return res;
}

/*************************************************************************
 * 函数名称：modem_cell_data_real_flush
 * 函数功能：刷新小区信息计时，快速重新获取小区信息
 * 输入参数：void
 * 返 回 值：void
 *************************************************************************/
void modem_cell_data_real_flush(void)
{
    misc_cps->cell_upd_last_time = NOTIMING;
    cell_info_update();
}

/*************************************************************************
 * 函数名称：modem_get_ipaddr_info
 * 函数功能：获取PDP地址
 * 输入参数：void
 * 返 回 值：负数-获取失败，0-获取成功
 *************************************************************************/
int32_t modem_get_ipaddr_info(struct local_ipaddr *ipaddr)
{
    int32_t res = 0;

    if (!misc_cps->is_ipaddr_upd_success) {
        res = -ENODATA;
    } else {
        if (NULL != ipaddr)
            memcpy(ipaddr, &misc_cps->ipaddr, sizeof(struct local_ipaddr));
    }

    return res;
}

/*************************************************************************
 * 函数名称：ntp_process
 * 函数功能：NTP校时处理
 * 输入参数：void
 * 返 回 值：void
 *************************************************************************/
void ntp_process(void)
{
    static uint32_t ntp_time = 0;
    static unsigned int sync_cnt = 0;
    unsigned int wait_time;

    /* 上电后必须从Modem同步一次获取当地时区，否则判断为未完成同步 */
    if(0 != (get_time_sync_flg() & 2) ) {                              /* 已同步过 */
        sync_cnt = 0;
        return;
    } else {
        misc_cps->rtc_had_sync_net = false;
    }

    if (NETREG_NO == misc_cps->net_status)
        return;

    wait_time = sync_cnt < 3 ? 10 : 3600; 
    if (0 == ntp_time || timeout_sec(ntp_time,  wait_time)) {
        qltask_modem_ioctl(MDM_CFG_NTP, "time.windows.com", 123);       /* 配置NTP服务器 */
        if (0 == qltask_modem_ntp_sync())                               /* 请求MODEM执行一次校时 */
            log_i("start ntp success\r\n");
        else 
            log_i("start ntp failed\r\n");

        sync_cnt++;
        ntp_time = getsec();
    }
}

/*******************************************************************************
 * 函数名称：rtc_sync_over_net
 * 功能描述：当前系统RTC时间是已跟NET UTC时间同步
 * 输入参数：void
 * 返 回 值：true 已同步  false 未同步
 ******************************************************************************/
bool rtc_sync_over_net(void)
{
    return misc_cps->rtc_had_sync_net;
}

/*******************************************************************************
 * 函数名称：modem_utc_update_rtc_time
 * 功能描述：用MODEM获取的UTC时间更新RTC时间（只是作为辅助GPS校时使用）
 * 注     意：如果已经利用GPS更新了RTC时间，则不再使用MODEM提供的UTC
 * 输入参数：utc[I] 从modem获取的utc时间
             timezone[I]: 是否需要减去时区
 * 返 回 值：void
 ******************************************************************************/
void modem_utc_update_rtc_time(struct tm *utc, int32_t timezone)
{
	rtc_update_from_modem(utc, timezone);
    misc_cps->rtc_had_sync_net = true;
}

/*-------------------------------------------------------------------------------------------------------------------*/
void mmgr_misc_init(void)
{
    misc_cps_restore_defult();
}

void mmgr_misc_process(void)
{
    netreg_status_update();     /* 联网状态定时查询 */
    csq_status_update();        /* 信号质量定时查询 */
    cell_info_update();         /* 小区信息定时查询 */
    pdp_ip_update();            /* PDP地址定时查询 */
    ntp_process();              /* NTP校时 */
}

/*************************************************************************
 * 函数名称：mmgr_misc_free
 * 函数功能：重置所有查询信息
 * 输入参数：void
 * 返 回 值：void
 *************************************************************************/
void mmgr_misc_free(void)
{
    misc_cps_restore_defult();
}